package com.erhya.export.poi;

import jxl.Workbook;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.VerticalAlignment;
import jxl.format.*;
import jxl.write.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.Boolean;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 基于JXL实现的模板方法
 * @param <T> 数据泛型
 * @author lilin  模板模式 + 泛型 + 反射
 */
public class JxlExcelTemplate<T> extends AbstractExcelTemplate<T> implements ExecuteChain<T> {

    /** excel的文档对象 */
    private WritableWorkbook workbook;
    /** excel的表单 */
    private WritableSheet sheet;
    /** excel的表单名称 */
    private String sheetName;

    /** 标题 */
    private String[][] title;
    /** 标题中最长的一行 */
    private Integer titleMaxLen;
    /** 标题栏的合并列 */

    /** 默认单元格宽度 */
    private static final Integer DEFAULT_WIDTH = 12;

    /** 导出文件名 */
    private String fileName;
    /** 标题栏 样式 */
    private CellFormat titleStyle;
    /** 数据栏 样式 */
    private CellFormat dataStyle;

    /** 列表数据 */
    private List<T> data;
    /** 列表数据展示字段 */
    private String[] showFiles;
    /** 当前数据行（标题行数+已写入数据行） */
    private Integer startIndex;

    private boolean autoExport;
    private Integer hasRows;

    public JxlExcelTemplate(String fileName, String[][] title, String sheetName) {
        this.fileName = fileName;
        this.title = title;
        this.sheetName = sheetName;
        this.autoExport = true;
    }

    public JxlExcelTemplate(String fileName, String[][] title, String sheetName, Boolean autoExport) {
        this.fileName = fileName;
        this.title = title;
        this.sheetName = sheetName;
        this.autoExport = autoExport;
    }

    private void init(WritableWorkbook workbook){
        try {
            if (workbook == null){
                ServletOutputStream ops = getResponse().getOutputStream();
                workbook = Workbook.createWorkbook(ops);
            }
            this.workbook = workbook;
            this.titleStyle = buildTitleStyle();

            if (StringUtils.isEmpty(sheetName)){
                sheetName = "Sheet";
            }

            this.sheet = workbook.getSheet(sheetName);
            if (this.sheet == null){
                this.sheet = workbook.createSheet(sheetName, 0);
            }
            super.counter = new AtomicInteger(0);
            this.startIndex = this.title == null ? 0 : this.title.length;
            this.dataStyle = buildColumStyle();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 标题行初始化 + 合并
     * @throws Exception
     */
    private void titleInit() throws Exception {
        if (this.title == null || this.title.length == 0){
            throw new NullPointerException("title can not null");
        }

        if (titleMaxLen == null){
            titleMaxLen = 0;
        }

        for (String[] t1 : title) {
            int length = t1.length;
            titleMaxLen = Math.max(length, titleMaxLen);
        }

        this.hasRows = sheet.getRows();

        for (int i = 0; i < title.length; i++) {
            String[] t2 = title[i];
            Integer mergeCell = 0;
            String tag = null;

            for (int j = 0; j < t2.length; j++) {
                String cell2 = t2[j];
                if (t2.length == 1){
                    sheet.addCell(new Label(j, i+hasRows, cell2, buildTitleStyle()));
                    sheet.mergeCells(j, i+hasRows, titleMaxLen - 1, i+hasRows);
                }else {
                    sheet.addCell(new Label(j, i+hasRows, cell2, buildTitleStyle()));
                    if (tag == null){
                        tag = cell2;
                    }

                    if (tag.equals(cell2)){
                        mergeCell++;
                    } else {
                        if(mergeCell > 1){
                            sheet.mergeCells(j-mergeCell, i+hasRows, j-1, i+hasRows);
                        }
                        tag = cell2;
                        mergeCell = 1;
                    }

                    // 最后一行合并方式和其他不一样
                    if (j == t2.length-1 && tag.equals(cell2) && mergeCell > 1){
                        sheet.mergeCells(j-mergeCell+1, i+hasRows, j, i+hasRows);
                    }
                }
            }
        }
    }

    protected CellFormat buildTitleStyle(){
        WritableCellFormat wcfFC = new WritableCellFormat();
        try {
            wcfFC.setVerticalAlignment(VerticalAlignment.CENTRE);
            wcfFC.setAlignment(Alignment.CENTRE);
            wcfFC.setBorder(Border.ALL, BorderLineStyle.THIN);
            wcfFC.setBackground(Colour.GREY_25_PERCENT);
            WritableFont writableFont = new WritableFont(WritableFont.TIMES, 12, WritableFont.BOLD);
            wcfFC.setFont(writableFont);
        } catch (WriteException e) {
            e.printStackTrace();
        }
        return wcfFC;
    }

    protected WritableCellFormat buildColumStyle() {
        WritableCellFormat wcfFC = new WritableCellFormat();
        try {
            wcfFC.setVerticalAlignment(VerticalAlignment.CENTRE);
            wcfFC.setAlignment(Alignment.LEFT);
            wcfFC.setBorder(Border.ALL, BorderLineStyle.THIN);
        } catch (WriteException e) {
            e.printStackTrace();
        }
        return wcfFC;
    }

    /**
     * 可重写的导出数据表格
     */
    protected void buildList(List<T> list, String... showFiles) throws Exception {
        try {
            Integer sheetIndex = 2;
            for (T t : list) {
                clazz = t.getClass();
                obj = t;

                // 判断是否需要拆分多个Sheet
                if (fetchSize != null && startIndex >= (fetchSize + title.length)){
                    startIndex = this.title.length;
                    this.sheet = workbook.createSheet(this.sheetName+sheetIndex, sheetIndex-1);
                    sheetIndex++;
                    titleInit();
                }

                int j = 0;
                for (String showFile : showFiles) {
                    // 获取字段值
                    String fieldVal = getObjectVal(showFile);
                    Label label = new Label(j, startIndex + hasRows, fieldVal, dataStyle);

                    WritableCellFeatures cellFeatures = null;
                    Map<String, Map<String, String>> dictMap = getDict();
                    if (cellSelect != null && cellSelect && !CollectionUtils.isEmpty(dictMap) && dictMap.containsKey(showFile)){
                        Map<String, String> dictValues = dictMap.get(showFile);
                        cellFeatures = new WritableCellFeatures();
                        cellFeatures.setDataValidationList(dictValues.values());
                    }

                    if (cellFeatures != null){
                        label.setCellFeatures(cellFeatures);
                    }
                    this.sheet.addCell(label);
                    this.sheet.setColumnView(j, getCellWidth(DEFAULT_WIDTH));

                    j++;
                }
                startIndex++;
            }
        } catch (WriteException e) {
            e.printStackTrace();
        }
    }

    public void exportList() {
        HttpServletResponse response = getResponse();
        response.reset();
        response.setContentType("application/json;charset=utf-8");
        try {
            response.setHeader("Content-disposition", "attachment; filename="+ new String((fileName).getBytes("gbk"),"iso8859-1") +".xls");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        BufferedOutputStream bufferedOutPut = null;
        ServletOutputStream output = null;
        try {
            output = response.getOutputStream();
            bufferedOutPut = new BufferedOutputStream(output);
            bufferedOutPut.flush();
            workbook.write();
        } catch (IOException e){
            e.printStackTrace();
        } finally{
            if (workbook != null){
                try {
                    workbook.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                } catch (WriteException e) {
                    throw new RuntimeException(e);
                }
            }

            if (bufferedOutPut != null){
                try {
                    bufferedOutPut.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (output != null){
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            delDict();
        }
    }

    @Override
    public void execute(List<T> exportList, String... showFiles){
        try {
            this.showFiles = showFiles;
            init(workbook);
            titleInit();
            buildList(exportList, showFiles);
            if (autoExport){
                exportList();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void unExecute(List<T> list, String... showFiles) {
        this.data = list;
        this.showFiles = showFiles;
    }

    @Override
    public void china(String fileName, Boolean flag) {
        try {
            init(workbook);
            titleInit();
            buildList(data, showFiles);
            if (!flag){
                exportList();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public WritableWorkbook getWorkbook() {
        return this.workbook;
    }

    public void setWorkbook(WritableWorkbook workbook) {
        this.workbook = workbook;
    }

    public HttpServletResponse getResponse() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getResponse();
    }
}
